﻿package com.iparticle {
	
	import flash.display.*;
	import flash.events.*;
	import flash.geom.*;
	import flash.utils.*;	
		
	public class ParticleEmitter extends Sprite {
		
		// Constants:
		
		//EVENTS:
		public static const EMIT:String = "emit";
		
		// EMITTER TYPES:
		public static const EXPLODER:String = "exploder";
		public static const FILL:String = "fill";
		public static const ANGULAR:String = "angular";
		
		// Events:		
		public static const NO_PARTICLES:String = "no_particles";
		
		// Defaults: 		
		private const DEFAULT_RATE:Number = .033;
		private const DEFAULT_VELOCITIES:Array = [5, 10];
		private const DEFAULT_GRAVITY:Number = .08;
		private const DEFAULT_SIZE_OFFSET:Number = 0;
		private const DEFAULT_LIFE:Number = 1;
		private const DEFAULT_SCALE_OFFSET:Number = 0;
		private const DEFAULT_ALPHAS:Array = [.2, .8];
		private const DEFAULT_EMIT_COUNT:int = 1;
		
		// ANGULAR Properties:
		public var angles:Array = [0, Math.PI];
		
		//FILL Properties:
		public var fillRect:Rectangle = new Rectangle(0,0,20,20);
		
		
		// Public Properties:
		public var emitEvent:Boolean = false; //Does emitter dispatch an event when a particle is emitted?
		public var particleClass:Class = DefaultParticle;  //Particle class to be used;
		public var particles:Array = [DefaultParticle]; //Array of particles to be used;
		public var alphas:Array = DEFAULT_ALPHAS; //Array of 2 numbers for start and end alpha in and out peercents
		public var scales:Array; //Array of 2 numbers for start and end scale values
		public var scaleOffset:Number = DEFAULT_SCALE_OFFSET; //Maximum scale offset
		public var velocities:Array = DEFAULT_VELOCITIES; //Array of 2 numbers for start and end velocties
		public var emitCount:int = DEFAULT_EMIT_COUNT;
		public var inertiaX:Number = 0; //X Inertia 
		public var inertiaY:Number = 0; //Y Inertia
		public var type:String = EXPLODER; //Emitter Type
		public var rate:Number = DEFAULT_RATE; //Rate of particles being emitted in milliseconds
		public var life:Number = DEFAULT_LIFE; //Life of a particle in seconds
		public var gravity:Number = DEFAULT_GRAVITY //Gravity, positive numbers = downward force, measured in pixels per cycle
		public var orient:Boolean = false; //Orient to current direction (processor intensive!)
		
		public static var fps:Number = 30; //Frames Per Second
		public static var spf:Number = 1/fps; //Seconds Per Frame
		public static var mpf:Number = 1000/fps; //Milliseconds Per Frame
		
		// Private Properties:
		private var _emitter:Point;
		private var _timer:Timer;
		private var _particles:Array;
		
		
		// Initialization:
		public function ParticleEmitter ( $props : Object = null ) { 
		
			
			for ( var prop:String in $props ) {
				if(prop in this){
					//property exists and is set
					this[prop] = $props[prop];
				}
			}
			
			
			_emitter = new Point();
			
			_timer = new Timer(Math.round(rate*1000));
			_timer.addEventListener(TimerEvent.TIMER, timerHandler);
			
			_particles = [];
			
			addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
			mouseEnabled = false;
			mouseChildren = false;
			
			addEventListener(Event.ENTER_FRAME, enterFrameHandler);
		}
	
		// Public Methods:
				
		public function start ( ) : void {
			_timer.start();
		}
		
		public function stop ( ) : void {
			_timer.stop();	
		}
		
		public function emit ( $number : int = 1 ) : void {
			//Call this function to emit a specific number of particles
			for ( var i:int = 0; i < $number; i++){
				createParticle();
			}
		}
		
		
		public function removeAllParticles ( ) : void {
			for each ( var particle : DisplayObject in _particles ){
				removeChild(particle);
			}
			
			_particles = [];
		}
		
		public function set emitterX ( $x : Number ) : void {
			_emitter.x = $x;
		}
		
		public function get emitterX ( ) : Number {
			return _emitter.x;
		}
		
		public function set emitterY ( $y : Number ) : void {
			_emitter.y = $y;
		}
		
		public function get emitterY ( ) : Number {
			return _emitter.y;
		}
		// Protected Methods:
		
		private function removeParticle ( $particle : Particle ) : void {
			removeChild($particle);
			_particles.splice(_particles.indexOf($particle), 1);
			
			if(_particles.length == 0){
				this.dispatchEvent(new Event(NO_PARTICLES));
			}
		}
		
		private function timerHandler ( $e : TimerEvent ) : void {
			for (var i:int = 0; i<emitCount; i++){
				createParticle();
			}
		}
		
		private function createParticle ( ) : void {
			var particle:Particle;
			if(particles.length > 1){
				particle = new particles[Math.floor(Math.random()*particles.length)];
			} else {
				particle = new particleClass;
			}
			//var 
			particle.x = _emitter.x;
			particle.y = _emitter.y;
			
			var randVelocity:Number = velocities[0] + (velocities[1]-velocities[0])*Math.random();
			var startScale:Number = 1;
			var endScale:Number = 1;
			var speeds:Point;
						
			if(scales){
				startScale = scales[0] - scaleOffset + (scaleOffset*2*Math.random());
				endScale = scales[1] - scaleOffset + (scaleOffset*2*Math.random());
			}
			
			particle.alphaInPercent = alphas[0];
			particle.alphaOutPercent = alphas[1];
			particle.scaleX = particle.scaleY = startScale;
			particle.endScale = endScale;
			particle.orient = orient;
					
			switch(type){
				case EXPLODER: {
					speeds = ParticleMath.pointFromAngle(Math.random()*Math.PI*2, randVelocity);
					particle.xSpeed = speeds.x + inertiaX;
					particle.ySpeed = speeds.y + inertiaY;
					particle.gravity = gravity;
					break;
				}
				
				case FILL: {
					particle.xSpeed = inertiaX;
					particle.ySpeed = inertiaY;
					particle.x = fillRect.x + fillRect.width*Math.random();
					particle.y = fillRect.y + fillRect.height*Math.random();
					
				}
				
				case ANGULAR: {
					var randAngle:Number = angles[0] + (angles[1]-angles[0])*Math.random();
					speeds = ParticleMath.pointFromAngle(randAngle, randVelocity);
					particle.xSpeed = speeds.x + inertiaX;
					particle.ySpeed = speeds.y + inertiaY;
					particle.gravity = gravity;
										
				}
				
				
			}
			
			particle.life = life;
			particle.addEventListener(Event.COMPLETE, particleCompleteHandler);
				
			addChild(particle)
			_particles.push(particle);
			
			if(emitEvent){
				this.dispatchEvent(new Event(EMIT));
			}
			
		}
		
		private function particleCompleteHandler ( $e : Event ) : void {
			removeParticle(Particle($e.target));
		}
		
		private function enterFrameHandler ( $e : Event ) : void {
			for each ( var particle : Particle in _particles ) {
				particle.update();
			}
		}
		
		
		private function addedToStageHandler ( $e : Event ) : void {
			fps = stage.frameRate;
			spf = 1/fps;
			mpf = 1000/fps;
		}
		
	}
	
}